﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xrm.Sdk;
using Microsoft.Crm.Sdk.Messages;
using System.ServiceModel;
using System.Web.Services.Protocols;
using System.IO;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Messages;

namespace Microsoft.IT.Crm.Utility.CrmSolutionManager
{
    class XrmSolutionService
    {
        /// <summary>
        /// Export the solution with unique name, set the export managed tag.
        /// </summary>
        /// <param name="uniqueName"></param>
        /// <param name="managed"></param>
        /// <returns></returns>
        public static byte[] ExportSolution(string uniqueName, bool managed)
        {
            try
            {
                ExportSolutionRequest exportSolutionRequest = new ExportSolutionRequest();
                exportSolutionRequest.Managed = managed;
                exportSolutionRequest.SolutionName = uniqueName;

                ExportSolutionResponse exportSolutionResponse = (ExportSolutionResponse)XrmServiceHelper.OrganizationService.Execute(exportSolutionRequest);

                byte[] exportXml = exportSolutionResponse.ExportSolutionFile;
                return exportXml;
            }
            catch (FaultException<OrganizationServiceFault>)
            {
                throw;
            }

            catch (SoapException)
            {
                throw;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// import the solution according to the solution zip file
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static void ImportSolution(byte[] bytes)
        {
            try
            {
                ImportSolutionRequest importSolutionRequest = new ImportSolutionRequest();
                //importSolutionRequest.OverwriteUnmanagedCustomizations = true;
                importSolutionRequest.PublishWorkflows = true;
                importSolutionRequest.CustomizationFile = bytes;

                ImportSolutionResponse importSolutionResponse = (ImportSolutionResponse)XrmServiceHelper.OrganizationService.Execute(importSolutionRequest);
            }
            catch (FaultException<OrganizationServiceFault>)
            {
                throw;
            }

            catch (SoapException)
            {
                throw;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// create a new solution with the publisher.
        /// </summary>
        /// <param name="publisher"></param>
        /// <param name="solutionName"></param>
        /// <returns></returns>
        internal static Entity CreateSolution(Entity publisher, string solutionName)
        {
            if (publisher == null) return null;
            if (string.IsNullOrEmpty(solutionName)) return null;
            try
            {
                Entity result = new Entity(EntityConstants.Const_Solution_Entity);
                result.Attributes[EntityConstants.Const_Solution_Entity_UniqueName] = solutionName;
                result.Attributes[EntityConstants.Const_Solution_Entity_FriendlyName] = solutionName;
                result.Attributes[EntityConstants.Const_Solution_Entity_PublisherId] = new EntityReference(EntityConstants.Const_Publisher_Entity, publisher.Id);
                result.Attributes[EntityConstants.Const_Solution_Entity_Version] = "1.0.0.0";

                result.Id = XrmServiceHelper.OrganizationService.Create(result);

                return result;
            }
            catch (FaultException<OrganizationServiceFault>)
            {
                throw;
            }

            catch (SoapException)
            {
                throw;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// remove all the components from the current solution, will not delete any component.
        /// </summary>
        /// <param name="solution"></param>
        internal static void RemoveAllComponents(Entity solution)
        {
            if (solution == null) return;
            try
            {
                RetrieveMultipleRequest request = new RetrieveMultipleRequest()
                {
                    Query = new QueryExpression()
                        {
                            EntityName = EntityConstants.Const_SolutionComponent_Entity,
                            ColumnSet = new ColumnSet(true),
                            Criteria = new FilterExpression
                            {
                                FilterOperator = LogicalOperator.And,
                                Conditions = 
                                {
                                    new ConditionExpression(EntityConstants.Const_Solution_Entity_Id, ConditionOperator.Equal, solution.Id)
                                }
                            }
                        }
                };

                RetrieveMultipleResponse response = (RetrieveMultipleResponse)XrmServiceHelper.OrganizationService.Execute(request);

                foreach (var entity in response.EntityCollection.Entities)
                {

                    RemoveSolutionComponentRequest componentRequest = new RemoveSolutionComponentRequest();
                    componentRequest.SolutionUniqueName = solution.GetAttributeValue<string>(EntityConstants.Const_Solution_Entity_UniqueName);
                    componentRequest.ComponentId = entity.GetAttributeValue<Guid>(EntityConstants.Const_SolutionComponent_Entity_ObjectId);
                    componentRequest.ComponentType = entity.GetAttributeValue<OptionSetValue>(EntityConstants.Const_SolutionComponent_Entity_ComponentType).Value;
                    RemoveSolutionComponentResponse componentResponse = (RemoveSolutionComponentResponse)XrmServiceHelper.OrganizationService.Execute(componentRequest);
                }
            }
            catch (FaultException<OrganizationServiceFault>)
            {
                throw;
            }

            catch (SoapException)
            {
                throw;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// retrieve all the components from the given solution.
        /// </summary>
        /// <param name="solution"></param>
        /// <returns></returns>
        internal static DataCollection<Entity> RetrieveAllComponents(Entity solution)
        {
            if (solution == null) return null;
            try
            {
                RetrieveMultipleRequest request = new RetrieveMultipleRequest()
                {
                    Query = new QueryExpression()
                    {
                        EntityName = EntityConstants.Const_SolutionComponent_Entity,
                        ColumnSet = new ColumnSet(true),
                        Criteria = new FilterExpression
                        {
                            FilterOperator = LogicalOperator.And,
                            Conditions = 
                                {
                                    new ConditionExpression(EntityConstants.Const_Solution_Entity_Id, ConditionOperator.Equal, solution.Id)
                                }
                        }
                    }
                };

                RetrieveMultipleResponse response = (RetrieveMultipleResponse)XrmServiceHelper.OrganizationService.Execute(request);
                if (response != null && response.EntityCollection != null)
                    return response.EntityCollection.Entities;
                else
                    return null;
            }
            catch (FaultException<OrganizationServiceFault>)
            {
                throw;
            }

            catch (SoapException)
            {
                throw;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// retrieve all the web resources by the given ids.
        /// </summary>
        /// <param name="objectIds"></param>
        /// <returns></returns>
        internal static DataCollection<Entity> RetrieveAllWebResources(IEnumerable<string> objectIds)
        {
            if (objectIds == null) return null;
            try
            {
                RetrieveMultipleRequest request = new RetrieveMultipleRequest()
                {
                    Query = new QueryExpression()
                    {
                        EntityName = EntityConstants.Const_WebResource_Entity,
                        ColumnSet = new ColumnSet(EntityConstants.Static_WebResource_Key_Fields),
                        Criteria = new FilterExpression
                        {
                            FilterOperator = LogicalOperator.And,
                            Conditions = 
                            {
                                new ConditionExpression(EntityConstants.Const_WebResource_Entity_Id, ConditionOperator.In, objectIds.ToArray())
                            }
                        }
                    }
                };

                RetrieveMultipleResponse response = (RetrieveMultipleResponse)XrmServiceHelper.OrganizationService.Execute(request);
                if (response != null && response.EntityCollection != null)
                    return response.EntityCollection.Entities;
                else
                    return null;
            }
            catch (FaultException<OrganizationServiceFault>)
            {
                throw;
            }

            catch (SoapException)
            {
                throw;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// retrieve all the plugin message steps by the given ids.
        /// </summary>
        /// <param name="objectIds"></param>
        /// <returns></returns>
        internal static DataCollection<Entity> RetrieveAllPluginSteps(IEnumerable<string> objectIds)
        {
            if (objectIds == null) return null;
            try
            {
                RetrieveMultipleRequest request = new RetrieveMultipleRequest()
                {
                    Query = new QueryExpression()
                    {
                        EntityName = EntityConstants.Const_SdkMessageProcessingStep_Entity,
                        ColumnSet = new ColumnSet(EntityConstants.Static_SdkMessageProcessingStep_Key_Fields),
                        Criteria = new FilterExpression
                        {
                            FilterOperator = LogicalOperator.And,
                            Conditions = 
                            {
                                new ConditionExpression(EntityConstants.Const_SdkMessageProcessingStep_Entity_Id, ConditionOperator.In, objectIds.ToArray()),
                                new ConditionExpression(EntityConstants.Const_SdkMessageProcessingStep_Entity_Configuration, ConditionOperator.NotNull, null) 
                            }
                        }
                    }
                };

                RetrieveMultipleResponse response = (RetrieveMultipleResponse)XrmServiceHelper.OrganizationService.Execute(request);
                if (response != null && response.EntityCollection != null)
                    return response.EntityCollection.Entities;
                else
                    return null;
            }
            catch (FaultException<OrganizationServiceFault>)
            {
                throw;
            }

            catch (SoapException)
            {
                throw;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// publish the current solution with web resources changes.
        /// </summary>
        /// <param name="webResources"></param>
        internal static void PublishSolution(ICollection<Entity> webResources)
        {
            try
            {
                PublishXmlRequest request = new PublishXmlRequest()
                {
                    ParameterXml = string.Format("<importexportxml><webresources>{0}</webresources></importexportxml>", 
                        string.Join("", webResources.Select(p => string.Format("<webresource>{0}</webresource>", p.Id.ToString()))))
                };

                XrmServiceHelper.OrganizationService.Execute(request);
            }
            catch (FaultException<OrganizationServiceFault>)
            {
                throw;
            }

            catch (SoapException)
            {
                throw;
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
}
